home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xpaint-2.1.1 / PaintRegion.c < prev    next >
C/C++ Source or Header  |  1995-05-03  |  43KB  |  1,663 lines

  1. /* +-------------------------------------------------------------------+ */
  2. /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)            | */
  3. /* |                                                                   | */
  4. /* | Permission to use, copy, modify, and to distribute this software  | */
  5. /* | and its documentation for any purpose is hereby granted without   | */
  6. /* | fee, provided that the above copyright notice appear in all       | */
  7. /* | copies and that both that copyright notice and this permission    | */
  8. /* | notice appear in supporting documentation.  There is no           | */
  9. /* | representations about the suitability of this software for        | */
  10. /* | any purpose.  this software is provided "as is" without express   | */
  11. /* | or implied warranty.                                              | */
  12. /* |                                                                   | */
  13. /* +-------------------------------------------------------------------+ */
  14.  
  15. /*
  16. ** PaintRegion.c -- Hopefully all of the routines to get,set and  
  17. **   manipulate the selection region.
  18. **
  19. **  Not part of the "selection" operation, since this really
  20. **   need to know lots of hidden information (why?)
  21. */
  22.  
  23. #include <math.h>
  24. #include <X11/IntrinsicP.h>
  25. #include <X11/StringDefs.h>
  26. #include <X11/cursorfont.h>
  27. #include <X11/Xatom.h>
  28. #include <X11/Xaw/Grip.h>
  29. #include "PaintP.h"
  30.  
  31. #define SHAPE
  32.  
  33. #ifdef SHAPE
  34. #include <X11/extensions/shape.h>
  35. #endif
  36.  
  37. static void regionExpose(Widget, PaintWidget, XEvent *, Boolean *);
  38.  
  39. #define regionRedraw(pw)    regionExpose(pw->paint.region.child, pw, NULL, NULL)
  40.  
  41. #define BoolStr(flg)    ((flg) ? "True" : "False")
  42.  
  43. #undef INTERACTIVE
  44.  
  45. /*
  46. **  Border Width of child widget
  47. */
  48. #define BW    0
  49.  
  50. /*
  51. **  2x2 matrix stuff
  52. **
  53. */
  54.  
  55. #define XFORM(x,y,mat,nx,ny)    nx = mat[0][0] * x + mat[0][1] * y; \
  56.                 ny = mat[1][0] * x + mat[1][1] * y
  57. #define COPY_MAT(s,d)    d[0][0] = s[0][0]; d[0][1] = s[0][1]; d[1][0] = s[1][0]; d[1][1] = s[1][1]
  58.  
  59. #define INVERT_MAT(mat, inv) do {                            \
  60.         float    _d = 1.0 / (mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]);    \
  61.         (inv)[0][0] =  (mat)[1][1] * _d;                    \
  62.         (inv)[1][1] =  (mat)[0][0] * _d;                    \
  63.         (inv)[0][1] = -(mat)[0][1] * _d;                    \
  64.         (inv)[1][0] = -(mat)[1][0] * _d;                    \
  65.     } while (0)
  66. #define ZERO(v)        (((v) > -1e-5) && ((v) < 1e-5))
  67.  
  68. #ifndef MIN
  69. #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
  70. #endif
  71. #ifndef MAX
  72. #define MAX(a,b)        (((a) > (b)) ? (a) : (b))
  73. #endif
  74. #ifndef SIGN
  75. #define SIGN(a)        (((a) < 0) ? -1 : 1)
  76. #endif
  77. #ifndef ABS
  78. #define ABS(a)          ((a > 0) ? (a) : 0 - (a))
  79. #endif
  80.  
  81. #define PRINT_MAT(msg,mat) printf("matrix %s =\n\t%f %f\n\t%f %f\n", msg, \
  82.                 mat[0][0], mat[0][1], mat[1][0], mat[1][1])
  83. #define MKMAT(pw) do {                            \
  84.         pwMatrix    m;                    \
  85.         m[0][0] = pw->paint.region.scaleX;            \
  86.         m[1][1] = pw->paint.region.scaleY;            \
  87.         m[0][1] = m[1][0] = 0.0;                \
  88.         mm(pw->paint.region.rotMat, m, pw->paint.region.mat);    \
  89.     } while (0)
  90.         
  91. static pwMatrix    matIdentity = { { 1, 0 }, { 0, 1 } };
  92.  
  93. static void doCallbacks(PaintWidget pw, Boolean flag)
  94. {
  95.     PaintWidget    tpw = (pw->paint.paint == None) ? pw : (PaintWidget)pw->paint.paint;
  96.     int        i;
  97.  
  98.     XtCallCallbackList((Widget)tpw, tpw->paint.regionCalls, (XtPointer)flag);
  99.     for (i = 0; i < tpw->paint.paintChildrenSize; i++) {
  100.         PaintWidget    p = (PaintWidget)tpw->paint.paintChildren[i];
  101.         XtCallCallbackList((Widget)p, p->paint.regionCalls, (XtPointer)flag);
  102.     }
  103. }
  104.  
  105. static void mm(pwMatrix a, pwMatrix b, pwMatrix r)
  106. {
  107.     float    t00, t10, t01, t11;
  108.  
  109.     t00 = a[0][0] * b[0][0] + a[0][1] * b[1][0];
  110.     t01 = a[0][0] * b[0][1] + a[0][1] * b[1][1];
  111.     t10 = a[1][0] * b[0][0] + a[1][1] * b[1][0];
  112.     t11 = a[1][0] * b[0][1] + a[1][1] * b[1][1];
  113.  
  114.     r[0][0] = t00;
  115.     r[1][0] = t10;
  116.     r[0][1] = t01;
  117.     r[1][1] = t11;
  118. }
  119.  
  120. /*
  121. **   PwRegionSet -- set the active image region
  122. **     add handles and other useful things
  123. **     if the pix == None, use the current paint info in rect
  124. **     else use the pixmap.
  125. **
  126. */
  127. static void    buildSources(PaintWidget pw)
  128. {
  129.     if (pw->paint.region.sourceImg == NULL) {
  130.         pw->paint.region.sourceImg = XGetImage(XtDisplay(pw), 
  131.                             pw->paint.region.source, 0, 0, 
  132.                             pw->paint.region.orig.width, 
  133.                             pw->paint.region.orig.height, AllPlanes, ZPixmap);
  134.     }
  135.  
  136.     if (pw->paint.region.mask == None)
  137.         return;
  138.  
  139.     if (pw->paint.region.maskImg == NULL) {
  140.         pw->paint.region.maskImg = XGetImage(XtDisplay(pw), 
  141.                             pw->paint.region.mask, 0, 0, 
  142.                             pw->paint.region.orig.width, 
  143.                             pw->paint.region.orig.height, AllPlanes, ZPixmap);
  144.     }
  145. }
  146.  
  147. static void resizeImg(PaintWidget pw, pwMatrix inv, 
  148.             Pixmap *pix, XImage *pixSrc, Pixmap *msk, XImage *mskSrc, Pixmap *shape)
  149. {
  150.     int        width, height, depth;
  151.     int        x, y;
  152.     XImage        *pixDst, *mskDst, *shapeDst = NULL;
  153.     int        sourceW, sourceH;
  154.     int        ix, iy, fx, fy, cx, cy;
  155.     float        dx, dy, sx, sy;
  156.  
  157.     if (pixSrc == NULL || pix == NULL)
  158.         return;
  159.     if (pw->paint.region.rect.width == 0 || pw->paint.region.rect.height == 0)
  160.         return;
  161.  
  162.     /*
  163.     **  Construct the dest pixmap.
  164.     */
  165.  
  166.     if (*pix != None)
  167.         XFreePixmap(XtDisplay(pw), *pix);
  168.     if (*msk != None && mskSrc != NULL)
  169.         XFreePixmap(XtDisplay(pw), *msk);
  170.  
  171.     depth = pixSrc->depth;
  172.  
  173.     width  = pw->paint.region.rect.width  / GET_ZOOM(pw);
  174.     height = pw->paint.region.rect.height / GET_ZOOM(pw);
  175.  
  176.     *pix   = XCreatePixmap(XtDisplay(pw), XtWindow(pw), width, height, depth);
  177.     pixDst = XCreateImage(XtDisplay(pw), pw->paint.visual, 
  178.                 depth, ZPixmap, 0, NULL, width, height, 32, 0);
  179.     pixDst->data = (char*)XtMalloc(height * pixDst->bytes_per_line);
  180.  
  181.     if (mskSrc != NULL) {
  182.         *msk = XCreatePixmap(XtDisplay(pw), XtWindow(pw), width, height, 1);
  183.         mskDst = XCreateImage(XtDisplay(pw), pw->paint.visual, 
  184.                     1, ZPixmap, 0, NULL, width, height, 32, 0);
  185.         mskDst->data = (char*)XtMalloc(height * mskDst->bytes_per_line);
  186. #ifdef SHAPE
  187.         if (shape != NULL) {
  188.             *shape = XCreatePixmap(XtDisplay(pw), XtWindow(pw), width, height, 1);
  189.             shapeDst = XCreateImage(XtDisplay(pw), pw->paint.visual, 
  190.                         1, ZPixmap, 0, NULL, width, height, 32, 0);
  191.             shapeDst->data = (char*)XtMalloc(height * shapeDst->bytes_per_line);
  192.         }
  193. #endif
  194.     }
  195.  
  196.  
  197.     cx = pw->paint.region.orig.width  / 2;
  198.     cy = pw->paint.region.orig.height / 2;
  199.     fx = (int)(-width  / 2);
  200.     fy = (int)(-height / 2);
  201.     sourceW = pixSrc->width;
  202.     sourceH = pixSrc->height;
  203.     for (y = 0, dy = fy; y < height; y++, dy++) {
  204.         for (x = 0, dx = fx; x < width; x++, dx++) {
  205.             XFORM(dx,dy,inv,sx,sy);
  206.             ix = (sx + cx);
  207.             iy = (sy + cy);
  208.             if (ix >= 0 && ix < sourceW && iy >= 0 && iy < sourceH) {
  209.                 xxPutPixel(pixDst, x, y, xxGetPixel(pixSrc, ix, iy));
  210.                 if (mskSrc != NULL)
  211.                     XPutPixel(mskDst, x, y, XGetPixel(mskSrc, ix, iy));
  212. #ifdef SHAPE
  213.                 if (shapeDst != NULL) 
  214.                     XPutPixel(shapeDst, x, y, True);
  215. #endif
  216.             } else if (mskSrc != NULL) {
  217.                 XPutPixel(mskDst, x, y, False);
  218. #ifdef SHAPE
  219.                 if (shapeDst != NULL) 
  220.                     XPutPixel(shapeDst, x, y, False);
  221. #endif
  222.             }
  223.         }
  224.     }
  225.  
  226.     XPutImage(XtDisplay(pw), *pix,  pw->paint.tgc, pixDst, 0, 0, 0, 0, width, height);
  227.     XDestroyImage(pixDst);
  228.  
  229.     if (mskSrc != NULL) {
  230.         XPutImage(XtDisplay(pw), *msk, pw->paint.mgc, mskDst, 0, 0, 0, 0, width, height);
  231.         XDestroyImage(mskDst);
  232.     }
  233. #ifdef SHAPE
  234.     if (shapeDst != NULL) {
  235.         XPutImage(XtDisplay(pw), *shape, pw->paint.mgc, shapeDst, 0, 0, 0, 0, width, height);
  236.         XDestroyImage(shapeDst);
  237.     }
  238. #endif
  239. }
  240.  
  241. static void regionCreateNotMask(PaintWidget pw)
  242. {
  243.     if (pw->paint.region.rect.width == 0 || pw->paint.region.rect.height == 0)
  244.         return;
  245.  
  246.     if (pw->paint.region.mask == None)
  247.         return;
  248.  
  249.     if (pw->paint.region.notMask != None)
  250.         XFreePixmap(XtDisplay(pw), pw->paint.region.notMask);
  251.  
  252.     pw->paint.region.notMask = XCreatePixmap(XtDisplay(pw), XtWindow(pw), 
  253.                     pw->paint.region.rect.width, pw->paint.region.rect.height, 1);
  254.     XSetFunction(XtDisplay(pw), pw->paint.mgc, GXcopyInverted);
  255.     XCopyArea(XtDisplay(pw), pw->paint.region.mask, pw->paint.region.notMask, pw->paint.mgc,
  256.             0, 0, pw->paint.region.rect.width, pw->paint.region.rect.height, 0, 0);
  257.     XSetFunction(XtDisplay(pw), pw->paint.mgc, GXcopy);
  258.  
  259.     XSetClipMask(XtDisplay(pw), pw->paint.region.bg_gc, pw->paint.region.notMask);
  260.     XSetClipMask(XtDisplay(pw), pw->paint.region.fg_gc, pw->paint.region.mask);
  261. }
  262.  
  263. static void createVtxPts(PaintWidget pw, float vtx[9][2], Boolean flag, Boolean useZoom)
  264. {
  265.     int    zoom;
  266.     int    i;
  267.     int    x0, x1, y0, y1;
  268.     int    width  = pw->paint.region.orig.width;
  269.     int    height = pw->paint.region.orig.height;
  270.  
  271.     if (useZoom)
  272.         zoom = GET_ZOOM(pw);
  273.     else
  274.         zoom = 1;
  275.  
  276.     x0 = (-width / 2);
  277.     x1 = ( width + x0);
  278.     y0 = (-height / 2);
  279.     y1 = ( height + y0);
  280.     x0 *= zoom;
  281.     x1 *= zoom;
  282.     y0 *= zoom;
  283.     y1 *= zoom;
  284.  
  285.     /*
  286.     **  Watch out, these are points 0,1, and _3__
  287.     */
  288.     XFORM(x0,y0,pw->paint.region.mat,vtx[0][0],vtx[0][1]);
  289.     XFORM(x1,y0,pw->paint.region.mat,vtx[1][0],vtx[1][1]);
  290.     XFORM(x0,y1,pw->paint.region.mat,vtx[3][0],vtx[3][1]);
  291.  
  292.     if (flag) {
  293.         XFORM(x1,y1,pw->paint.region.mat,vtx[2][0],vtx[2][1]);
  294.  
  295.         for (i = 0; i < 4; i++) {
  296.             vtx[i][0] += pw->paint.region.centerX * zoom;
  297.             vtx[i][1] += pw->paint.region.centerY * zoom;
  298.         }
  299.     } else {
  300.         /*
  301.         **  sort the points, so that point 0,0 is top left corner
  302.         */
  303.         if (vtx[0][0] > vtx[1][0]) {
  304.             float    t = x0;
  305.             x0 = x1;
  306.             x1 = t;
  307.         }
  308.         if (vtx[0][1] > vtx[3][1]) {
  309.             float    t = y0;
  310.             y0 = y1;
  311.             y1 = t;
  312.         }
  313.  
  314.         XFORM(x0,y0,pw->paint.region.mat,vtx[0][0],vtx[0][1]);
  315.         XFORM( 0,y0,pw->paint.region.mat,vtx[1][0],vtx[1][1]);
  316.         XFORM(x1,y0,pw->paint.region.mat,vtx[2][0],vtx[2][1]);
  317.  
  318.         XFORM(x0, 0,pw->paint.region.mat,vtx[3][0],vtx[3][1]);
  319.         XFORM( 0, 0,pw->paint.region.mat,vtx[4][0],vtx[4][1]);
  320.         XFORM(x1, 0,pw->paint.region.mat,vtx[5][0],vtx[5][1]);
  321.  
  322.         XFORM(x0,y1,pw->paint.region.mat,vtx[6][0],vtx[6][1]);
  323.         XFORM( 0,y1,pw->paint.region.mat,vtx[7][0],vtx[7][1]);
  324.         XFORM(x1,y1,pw->paint.region.mat,vtx[8][0],vtx[8][1]);
  325.     }
  326. }
  327.  
  328. static void doResize(PaintWidget pw)
  329. {
  330.     pwMatrix    inv;
  331.     Pixmap        shape, *shp = &shape;
  332.  
  333.     buildSources(pw);
  334.  
  335.     /*
  336.     **  First find out the bounding extent of the transformed
  337.     **   area, then scale it to fit inside of the "region box"
  338.     */
  339.  
  340.     if (pw->paint.region.maskImg == NULL) {
  341.         Boolean        needMask = False;
  342.         float        vtx[9][2];
  343.         float        minX, minY, maxX, maxY;
  344.         int        x, cmin, cmax;
  345.  
  346.         createVtxPts(pw, vtx, True, False);
  347.  
  348.         minX = MIN(vtx[0][0],MIN(vtx[1][0],MIN(vtx[2][0],vtx[3][0])));
  349.         minY = MIN(vtx[0][1],MIN(vtx[1][1],MIN(vtx[2][1],vtx[3][1])));
  350.         maxX = MAX(vtx[0][0],MAX(vtx[1][0],MAX(vtx[2][0],vtx[3][0])));
  351.         maxY = MAX(vtx[0][1],MAX(vtx[1][1],MAX(vtx[2][1],vtx[3][1])));
  352.  
  353.         /*
  354.         **  After computing min, max see if there are points
  355.         **   on all vertices, if so then set the correct return code
  356.         */
  357.         for (cmin = cmax = x = 0; x < 4; x++) {
  358.             if ((int)vtx[x][0] == (int)minX)
  359.                 cmin++;
  360.             else if ((int)vtx[x][0] == (int)maxX)
  361.                 cmax++;
  362.         }
  363.         needMask |= (cmin != 2 || cmax != 2);
  364.         for (cmin = cmax = x = 0; x < 4; x++) {
  365.             if ((int)vtx[x][1] == (int)minY)
  366.                 cmin++;
  367.             else if ((int)vtx[x][1] == (int)maxY)
  368.                 cmax++;
  369.         }
  370.         needMask |= (cmin != 2 || cmax != 2);
  371.  
  372.         if (needMask) {
  373.             Pixmap    mask;
  374.             GC    mgc;
  375.  
  376.             /*
  377.             **  If the image we just transformed needs a mask
  378.             **  and one doesn't exist, construct one.
  379.             */
  380.             mask = pw->paint.region.mask = XCreatePixmap(XtDisplay(pw), XtWindow(pw), 
  381.                     pw->paint.region.orig.width, pw->paint.region.orig.height, 1);
  382.             mgc = GET_MGC(pw, mask);
  383.             XSetFunction(XtDisplay(pw), mgc, GXset);
  384.             XFillRectangle(XtDisplay(pw), mask, mgc, 0, 0,
  385.                         pw->paint.region.orig.width, 
  386.                         pw->paint.region.orig.height);
  387.             XSetFunction(XtDisplay(pw), mgc, GXcopy);
  388.             
  389.             buildSources(pw);
  390.  
  391.             if (pw->paint.region.fg_gc == None) {
  392.                 pw->paint.region.fg_gc = XCreateGC(XtDisplay(pw), XtWindow(pw), 0, 0);
  393.                 pw->paint.region.bg_gc = XCreateGC(XtDisplay(pw), XtWindow(pw), 0, 0);
  394.             }
  395.         }
  396.     }
  397.  
  398.     INVERT_MAT(pw->paint.region.mat, inv);
  399. #ifdef SHAPE
  400.     if (pw->paint.region.maskImg == NULL || GET_ZOOM(pw) != 1)
  401.         shp = NULL;
  402. #else
  403.     shp = NULL;
  404. #endif
  405.     resizeImg(pw, inv, &pw->paint.region.source, pw->paint.region.sourceImg,
  406.             &pw->paint.region.mask, pw->paint.region.maskImg, shp);
  407.     regionCreateNotMask(pw);
  408.  
  409. #ifdef SHAPE
  410.     if (shp != NULL) {
  411.         XShapeCombineMask(XtDisplay(pw), XtWindow(pw->paint.region.child), 
  412.                     ShapeBounding, 0, 0, shape, ShapeSet);
  413.         XFreePixmap(XtDisplay(pw), shape);
  414.     }
  415. #endif
  416.  
  417.     pw->paint.region.needResize = False;
  418. }
  419.  
  420. static void drawRegionBox(PaintWidget pw, Boolean flag)
  421. {
  422.     static XPoint    xvtxLast[5];
  423.     XPoint        xvtx[5];
  424.     float        vtx[9][2];
  425.     Window        window = XtWindow(pw);
  426.     int        i;
  427.  
  428.     createVtxPts(pw, vtx, True, True);
  429.  
  430.     xvtx[0].x = vtx[0][0];    xvtx[0].y = vtx[0][1];
  431.     xvtx[1].x = vtx[1][0];    xvtx[1].y = vtx[1][1];
  432.     xvtx[2].x = vtx[2][0];    xvtx[2].y = vtx[2][1];
  433.     xvtx[3].x = vtx[3][0];    xvtx[3].y = vtx[3][1];
  434.     xvtx[4].x = vtx[0][0];    xvtx[4].y = vtx[0][1];
  435.  
  436.     for (i = 0; i < 5; i++) {
  437.         xvtx[i].x += pw->paint.region.child->core.x;
  438.         xvtx[i].y += pw->paint.region.child->core.y;
  439.     }
  440.  
  441.     if (pw->paint.region.isDrawn) {
  442.         XDrawLines(XtDisplay(pw), window, pw->paint.xgc, xvtxLast, 5, CoordModeOrigin);
  443.         pw->paint.region.isDrawn = False;
  444.     }
  445.  
  446.     if (flag) {
  447.         XDrawLines(XtDisplay(pw), window, 
  448.                 pw->paint.xgc, xvtx, 5, CoordModeOrigin);
  449.         memcpy(xvtxLast, xvtx, sizeof(xvtxLast));
  450.         pw->paint.region.isDrawn = True;
  451.     }
  452. }
  453.  
  454. /*
  455. **
  456. */
  457.  
  458. static void regionResizeWindow(PaintWidget pw, Boolean sameCenter)
  459. {
  460.     int    zoom = GET_ZOOM(pw);
  461.     int    minX, minY, maxX, maxY;
  462.     int    width, height, dx, dy, nx, ny;
  463.     int    newX, newY;
  464.     int    i;
  465.     float    vtx[9][2];
  466.  
  467. #ifndef INTERACTIVE
  468.     if (pw->paint.region.isTracking)
  469.         return;
  470. #endif
  471.  
  472.     createVtxPts(pw, vtx, False, False);
  473.  
  474.     minX = MIN(vtx[0][0], vtx[6][0]);
  475.     maxX = MAX(vtx[2][0], vtx[8][0]);
  476.     minY = MIN(vtx[0][1], vtx[2][1]);
  477.     maxY = MAX(vtx[6][1], vtx[8][1]);
  478.  
  479.     width  = maxX - minX + 0.5;
  480.     height = maxY - minY + 0.5;
  481.  
  482.     newX = pw->paint.region.centerX - (width  / 2);
  483.     newY = pw->paint.region.centerY - (height / 2);
  484.  
  485.     newX += pw->paint.region.rect.x;
  486.     newY += pw->paint.region.rect.y;
  487.     if (!sameCenter) {
  488.         pw->paint.region.centerX = width  / 2;
  489.         pw->paint.region.centerY = height / 2;
  490.     } else {
  491.         pw->paint.region.centerX = width / 2;
  492.         pw->paint.region.centerY = height / 2;
  493.     }
  494.  
  495.     if ((width  *= zoom) < 10)
  496.         width = 10;
  497.     if ((height *= zoom) < 10)
  498.         height = 10;
  499.     XtResizeWidget(pw->paint.region.child, width, height, BW);
  500.     nx = (newX - pw->paint.zoomX) * zoom;
  501.     ny = (newY - pw->paint.zoomY) * zoom;
  502.     XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
  503.  
  504.     pw->paint.region.rect.x      = newX;
  505.     pw->paint.region.rect.y      = newY;
  506.     pw->paint.region.rect.width  = width;
  507.     pw->paint.region.rect.height = height;
  508.  
  509.     /*
  510.     **  Now place all the grips.
  511.     */
  512.     createVtxPts(pw, vtx, False, True);
  513.     width  = pw->paint.region.grip[0]->core.width;
  514.     height = pw->paint.region.grip[0]->core.height;
  515.     dx     = pw->paint.region.centerX * zoom - width / 2;
  516.     dy     = pw->paint.region.centerY * zoom - height / 2;
  517.     for (i = 0; i < 9; i++) {
  518.         int    x, y;
  519.  
  520.         if (i == 4)
  521.             continue;
  522.  
  523.         x = vtx[i][0] + dx;
  524.         y = vtx[i][1] + dy;
  525.  
  526.         if (x < 0)
  527.             x = 0;
  528.         if (y < 0)
  529.             y = 0;
  530.         if (x + width > pw->paint.region.rect.width)
  531.             x = pw->paint.region.rect.width - width;
  532.         if (y + height > pw->paint.region.rect.height)
  533.             y = pw->paint.region.rect.height - height;
  534.  
  535.         XtMoveWidget(pw->paint.region.grip[i], x, y);
  536.     }
  537. }
  538.  
  539. /*
  540. **
  541. */
  542.  
  543. static void moveGrips(PaintWidget pw)
  544. {
  545.     int        width, height;
  546.     int        i, gx, gy;
  547.     Dimension    w, h;
  548.     Widget        widget = pw->paint.region.grip[0];
  549.  
  550.     w      = widget->core.width;
  551.     h      = widget->core.height;
  552.     width  = widget->core.parent->core.width;
  553.     height = widget->core.parent->core.height;
  554.  
  555.     for (i = 0; i < 9; i++) {
  556.         if (i == 4)
  557.             continue;
  558.  
  559.         switch (i % 3) {
  560.         case 0: gx = 0;                 break;
  561.         case 1: gx = width / 2 - w / 2; break;
  562.         case 2: gx = width - w;         break;
  563.         }
  564.         switch (i / 3) {
  565.         case 0: gy = 0;                  break;
  566.         case 1: gy = height / 2 - h / 2; break;
  567.         case 2: gy = height - h;         break;
  568.         }
  569.  
  570.         XtMoveWidget(pw->paint.region.grip[i], gx, gy);
  571.     }
  572. }
  573.  
  574. static void gripPress(Widget w, PaintWidget pw, XButtonEvent *event, Boolean *junk)
  575. {
  576.     static int    fixedPoint[] = { 8, 8, 6, 8, -1, 6, 2, 2, 0 };
  577.     float    vtx[9][2], fvtx[9][2];
  578.     float    x0, x1, x2, y0, y1, y2, t1, t2, l;
  579.     int    index, i;
  580.  
  581.     pw->paint.region.offX = event->x;
  582.     pw->paint.region.offY = event->y;
  583.     pw->paint.region.baseX = event->x_root - w->core.x;
  584.     pw->paint.region.baseY = event->y_root - w->core.y;
  585.  
  586.     pw->paint.region.isTracking = True;
  587.  
  588.     /*
  589.     **  Compute which grip was grabbed, to determine constrain line.
  590.     */
  591.     for (index = 0; index < 9 && pw->paint.region.grip[index] != w; index++)
  592.         ;
  593.  
  594.     createVtxPts(pw, vtx, False, False);
  595.     createVtxPts(pw, fvtx, True, False);
  596.  
  597.     x0 = vtx[0][0];        y0 = vtx[0][1];
  598.     x1 = vtx[2][0];        y1 = vtx[2][1];
  599.     x2 = vtx[6][0];        y2 = vtx[6][1];
  600.  
  601.     pw->paint.region.lineBase[0]  = 0;
  602.     pw->paint.region.lineBase[1]  = 0;
  603.  
  604.     t1 = x1 - x0;    t2 = y1 - y0;
  605.     l = sqrt(t1 * t1 + t2 * t2);
  606.     pw->paint.region.lineDelta[0] = t1 / l;
  607.     pw->paint.region.lineDelta[1] = t2 / l;
  608.  
  609.     t1 = x2 - x0;    t2 = y2 - y0;
  610.     l = sqrt(t1 * t1 + t2 * t2);
  611.     pw->paint.region.lineDelta[2] = t1 / l;
  612.     pw->paint.region.lineDelta[3] = t2 / l;
  613.  
  614.     pw->paint.region.startScaleX = pw->paint.region.scaleX;
  615.     pw->paint.region.startScaleY = pw->paint.region.scaleY;
  616.  
  617.     /*
  618.     **  Now compute which corner of the 4 cornerd box doesn't move
  619.     **    as the object is resized.
  620.     */
  621.     for (i = 0; i < 4; i++) {
  622.         float    fx = vtx[fixedPoint[index]][0];
  623.         float    fy = vtx[fixedPoint[index]][1];
  624.         float    px = fvtx[i][0] - pw->paint.region.centerX;
  625.         float    py = fvtx[i][1] - pw->paint.region.centerY;
  626.  
  627.         if (ZERO(fx - px) && ZERO(fy - py))
  628.             break;
  629.     }
  630.     pw->paint.region.fixedPoint = i;
  631. }
  632. static void regionButtonPress(Widget w, PaintWidget pw, XButtonEvent *event, Boolean *junk)
  633. {
  634.     pw->paint.region.isRotate = event->button == Button2;
  635.  
  636.     pw->paint.region.offX = event->x;
  637.     pw->paint.region.offY = event->y;
  638.     pw->paint.region.baseX = event->x_root - w->core.x;
  639.     pw->paint.region.baseY = event->y_root - w->core.y;
  640.  
  641.     if (pw->paint.region.isRotate) {
  642.         int    dx, dy;
  643.  
  644.         XDefineCursor(XtDisplay(w), XtWindow(pw->paint.region.child), XCreateFontCursor(XtDisplay(w), XC_exchange));
  645.  
  646.         dx = event->x - pw->paint.region.rect.width / 2;
  647.         dy = event->y - pw->paint.region.rect.height / 2;
  648.         pw->paint.region.lastAngle = atan2((double)dy, (double)dx);
  649.     }
  650.  
  651.     /*
  652.     **  Only draw the interactive box when we are rotating.
  653.     */
  654.     pw->paint.region.isTracking = pw->paint.region.isRotate;
  655. }
  656. static void gripRelease(Widget w, PaintWidget pw, XButtonEvent *event, Boolean *junk)
  657. {
  658.     pw->paint.region.isTracking = False;
  659.     drawRegionBox(pw, False);
  660. #ifndef INTERACTIVE
  661.     if (pw->paint.region.needResize) {
  662.         regionResizeWindow(pw, False);
  663.         regionRedraw(pw);
  664.     }
  665. #endif
  666. }
  667. static void regionButtonRelease(Widget w, PaintWidget pw, XButtonEvent *event, Boolean *junk)
  668. {
  669.     pw->paint.region.isTracking = False;
  670.     drawRegionBox(pw, False);
  671.  
  672.     if (!pw->paint.region.isRotate)
  673.         return;
  674.  
  675. #ifndef INTERACTIVE
  676.     if (pw->paint.region.needResize) {
  677.         regionResizeWindow(pw, False);
  678.         regionRedraw(pw);
  679.     }
  680. #endif
  681.  
  682.     XDefineCursor(XtDisplay(w), XtWindow(pw->paint.region.child), XCreateFontCursor(XtDisplay(w), XC_fleur));
  683. }
  684. static void regionGrab(Widget w, PaintWidget pw, XMotionEvent *event, Boolean *junk)
  685. {
  686.     int    dx, dy, nx, ny;
  687.  
  688.     while (XCheckTypedWindowEvent(XtDisplay(w), XtWindow(w), MotionNotify, (XEvent*)event));
  689.  
  690.     PwRegionTear((Widget)pw);
  691.  
  692.     if (pw->paint.region.isRotate) {
  693.         double        da, na;
  694.         pwMatrix    m;
  695.  
  696.         dx = event->x - pw->paint.region.rect.width / 2;
  697.         dy = event->y - pw->paint.region.rect.height / 2;
  698.         na = atan2((double)dy, (double)dx);
  699.         da = na - pw->paint.region.lastAngle;
  700.         pw->paint.region.lastAngle = na;
  701.  
  702.         m[0][0] =  cos(da);
  703.         m[0][1] = -sin(da);
  704.         m[1][0] =  sin(da);
  705.         m[1][1] =  cos(da);
  706.  
  707.         PwRegionAppendMatrix((Widget)pw, m);
  708.     } else {
  709.         int    zoom = GET_ZOOM(pw);
  710.  
  711.         nx = event->x_root - pw->paint.region.baseX;
  712.         ny = event->y_root - pw->paint.region.baseY;
  713.  
  714.         dx = (nx - w->core.x) / zoom;
  715.         dy = (ny - w->core.y) / zoom;
  716.  
  717.         if (dx == 0 && dy == 0)
  718.             return;
  719.  
  720.         pw->paint.region.rect.x += dx;
  721.         pw->paint.region.rect.y += dy;
  722.  
  723.         nx = (pw->paint.region.rect.x - pw->paint.zoomX) * zoom;
  724.         ny = (pw->paint.region.rect.y - pw->paint.zoomY) * zoom;
  725.  
  726.         XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
  727.     }
  728. }
  729. static void regionExpose(Widget w, PaintWidget pw, XEvent *event, Boolean *junk)
  730. {
  731.     XImage        *xim = NULL;
  732.     XRectangle    rect, nrect;
  733.     int        isExpose;
  734.     int        zoom = GET_ZOOM(pw);
  735.  
  736.     if (!pw->paint.region.isVisible)
  737.         return;
  738.  
  739.     if (event == NULL) {
  740.         isExpose = True;
  741.         rect.x = 0;
  742.         rect.y = 0;
  743.         rect.width = w->core.width;
  744.         rect.height = w->core.height;
  745.     } else if (event->xany.type == Expose) {
  746.         rect.x = event->xexpose.x;
  747.         rect.y = event->xexpose.y;
  748.         rect.width = event->xexpose.width;
  749.         rect.height = event->xexpose.height;
  750.         isExpose = True;
  751.     } else if (event->xany.type == ConfigureNotify) {
  752.         isExpose = False;
  753.         rect.x = 0;
  754.         rect.y = 0;
  755.         rect.width = w->core.width;
  756.         rect.height = w->core.height;
  757.     } else {
  758.         return;
  759.     }
  760.  
  761. #ifndef INTERACTIVE
  762.     if (pw->paint.region.isTracking) {
  763.         drawRegionBox(pw, True);
  764.         return;
  765.     }
  766. #endif
  767.     if (pw->paint.region.needResize)
  768.         doResize(pw);
  769.  
  770.     if (zoom != 1) {
  771.         int    width, height;
  772.         int    pixW, pixH;
  773.  
  774.         nrect.x      = rect.x / zoom;
  775.         nrect.y      = rect.y / zoom;
  776.         width        = (rect.width + zoom - 1) / zoom;
  777.         height       = (rect.height + zoom - 1) / zoom;
  778.         /*
  779.         **  It is possible for this top happen, when resizes and 
  780.         **    redraws are slightly out of sync.
  781.         */
  782.         pixW = pw->paint.region.rect.width / zoom;
  783.         pixH = pw->paint.region.rect.height / zoom;
  784.         if (width + nrect.x > pixW)
  785.             width = pixW - nrect.x;
  786.         if (height + nrect.y > pixH)
  787.             height = pixH - nrect.y;
  788.         if (width <= 0 || height <= 0)
  789.             return;
  790.         nrect.width  = width;
  791.         nrect.height = height;
  792.     }
  793.  
  794.     if (isExpose) {
  795.         if (zoom == 1) {
  796.             XCopyArea(XtDisplay(w), pw->paint.region.source, XtWindow(w), 
  797.                     pw->paint.region.fg_gc == None ? pw->paint.tgc : pw->paint.region.fg_gc,
  798.                     rect.x, rect.y, rect.width, rect.height, rect.x, rect.y);
  799.         } else  {
  800.             XImage        *src, *msk = NULL;
  801.             XRectangle    tr;
  802.  
  803.             tr = nrect;
  804.             tr.x = 0;
  805.             tr.y = 0;
  806.  
  807.             src = XGetImage(XtDisplay(pw), pw->paint.region.source, 
  808.                         nrect.x, nrect.y, nrect.width, nrect.height,
  809.                         AllPlanes, ZPixmap);
  810.             if (pw->paint.region.mask != None)
  811.                 msk = XGetImage(XtDisplay(pw), pw->paint.region.mask, 
  812.                         nrect.x, nrect.y, nrect.width, nrect.height,
  813.                         AllPlanes, ZPixmap);
  814.             _PwZoomDraw(pw, w, pw->paint.tgc, src, msk,
  815.                     False, nrect.x, nrect.y, zoom, &tr);
  816.             XDestroyImage(src);
  817.             if (msk != NULL)
  818.                 XDestroyImage(msk);
  819.         }
  820.     }
  821.  
  822.     /*
  823.     **  XXX -- This should merge, and do fun things.. but.
  824.     */
  825.     if (isExpose && (event != NULL && event->xexpose.count != 0))
  826.         return;
  827.  
  828.     if (pw->paint.region.mask != None) {
  829.         int    x = w->core.x + w->core.border_width;
  830.         int    y = w->core.y + w->core.border_width;
  831.  
  832.         /*
  833.         **  Copy in the background picture
  834.         */
  835.         if (GET_ZOOM(pw) != 1) {
  836.             XRectangle    tr;
  837.             XImage        *nmsk;
  838.             int        dx = 0, dy = 0;
  839.             PaintWidget    tpw = (pw->paint.paint == None) ? pw : (PaintWidget)pw->paint.paint;
  840.             int        width, height;
  841.  
  842.             tr.x = (nrect.x + x) / zoom + pw->paint.zoomX;
  843.             tr.y = (nrect.y + y) / zoom + pw->paint.zoomY;
  844.             width  = nrect.width;
  845.             height = nrect.height;
  846.  
  847.             /*
  848.             **  We could use PwGetImage, but it returns
  849.             **    the original image, not the sub-region
  850.             */
  851.             if (tr.x < 0) {
  852.                 width += tr.x;
  853.                 dx = -tr.x;
  854.                 tr.x = 0;
  855.             }
  856.             if (tr.y < 0) {
  857.                 height += tr.y;
  858.                 dy = -tr.y;
  859.                 tr.y = 0;
  860.             }
  861.             if (tr.x + width > tpw->paint.drawWidth) 
  862.                 width = tpw->paint.drawWidth - tr.x;
  863.             if (tr.y + height > tpw->paint.drawHeight) 
  864.                 height = tpw->paint.drawHeight - tr.y;
  865.             if (width > 0 && height > 0) {
  866.                 tr.width = width;
  867.                 tr.height = height;
  868.                 xim = XGetImage(XtDisplay(w), GET_PIXMAP(pw),
  869.                         tr.x, tr.y, tr.width, tr.height, 
  870.                         AllPlanes, ZPixmap);
  871.                 tr.x = tr.y = 0;
  872.                 nmsk = XGetImage(XtDisplay(pw), pw->paint.region.notMask, 
  873.                         nrect.x + dx, nrect.y + dy, tr.width, tr.height,
  874.                         AllPlanes, ZPixmap);
  875.  
  876.                 _PwZoomDraw(pw, w, pw->paint.tgc, xim, nmsk, False, 
  877.                         dx, dy, zoom, &tr);
  878.                 XDestroyImage(nmsk);
  879.                 XDestroyImage(xim);
  880.             }
  881.         } else {
  882.             XCopyArea(XtDisplay(w), GET_PIXMAP(pw), XtWindow(w), 
  883.                     pw->paint.region.bg_gc,
  884.                     x, y, w->core.width, w->core.height, 0, 0);
  885.         }
  886.     }
  887. }
  888. static void regionMove(Widget w, PaintWidget pw, XEvent *event, Boolean *junk)
  889. {
  890.     if (pw->paint.region.mask == None)
  891.         return;
  892.     /*
  893.     **  For some reason, I had to generate an extra function.. 
  894.     */
  895.     regionExpose(w, pw, event, junk);
  896. }
  897.  
  898.  
  899. static void regionSetGripCursors(PaintWidget pw)
  900. {
  901.     static int    cursors[9][2] = {
  902.                 { XC_top_left_corner, NorthWestGravity },
  903.                 { XC_top_side, NorthGravity },
  904.                 { XC_top_right_corner, NorthEastGravity },
  905.                 { XC_left_side, WestGravity },
  906.                 { 0, 0 },
  907.                 { XC_right_side, EastGravity },
  908.                 { XC_bottom_left_corner, SouthWestGravity },
  909.                 { XC_bottom_side, SouthGravity },
  910.                 { XC_bottom_right_corner, SouthEastGravity },
  911.             };
  912.     static int    list[9] = { None, None, None, None, None, None, None, None, None };
  913.     int        i;
  914.  
  915.     if (list[0] == None) {
  916.         for (i = 0; i < 9; i++) {
  917.             if (i != 4)
  918.                 list[i] = XCreateFontCursor(XtDisplay(pw), cursors[i][0]);
  919.         }
  920.     }
  921.  
  922.     for (i = 0; i < 9; i++)
  923.         if (i != 4)
  924.             XDefineCursor(XtDisplay(pw), XtWindow(pw->paint.region.grip[i]), list[i]);
  925. }
  926.  
  927. static void gripGrab(Widget w, PaintWidget pw, XMotionEvent *event, Boolean *junk)
  928. {
  929.     static Boolean    isLeftEdge[] = {True,  False, False,
  930.                     True,  False, False,
  931.                     True,  False, False };
  932.     static Boolean    isTopEdge[] = {    True,  True,  True,
  933.                     False, False, False,
  934.                     False, False, False };
  935.     static Boolean    isMiddle[] = {    False, True,  False,
  936.                     True,  False, True,
  937.                     False, True, False };
  938.     Boolean        sameScale;
  939.     int        index, i, fp;
  940.     int        zoom = GET_ZOOM(pw);
  941.     float        v[2];
  942.     int        width, height;
  943.     float        ovtx[9][2], nvtx[9][2];
  944.     float        dx, dy;
  945.  
  946.     for (index = 0; index < 9 && pw->paint.region.grip[index] != w; index++)
  947.         ;
  948.  
  949.     /*
  950.     **  Find the intersection point
  951.     */
  952.     for (i = 0; i < 2; i++) {
  953.         float    x0 = pw->paint.region.lineBase[0];
  954.         float    y0 = pw->paint.region.lineBase[1];
  955.         float    xm = event->x;
  956.         float    ym = event->y;
  957.  
  958.         dx = pw->paint.region.lineDelta[0 + i * 2];
  959.         dy = pw->paint.region.lineDelta[1 + i * 2];
  960.         v[i] = dx * (xm - x0) - dy * (y0 - ym);
  961.     }
  962.  
  963.     if (pw->paint.region.startScaleX < 0)
  964.         v[0] = -v[0];
  965.     if (pw->paint.region.startScaleY < 0)
  966.         v[1] = -v[1];
  967.     if (isLeftEdge[index])
  968.         v[0] = -v[0];
  969.     if (isTopEdge[index])
  970.         v[1] = -v[1];
  971.  
  972.     v[0] /= (float)zoom;
  973.     v[1] /= (float)zoom;
  974.  
  975.     PwRegionTear((Widget)pw);
  976.  
  977.     sameScale = False;
  978.     if (isMiddle[index] || (event->state & ShiftMask) != 0) {
  979.         /*
  980.         **  Apply the constraint
  981.         */
  982.         if (index == 1 || index == 7)
  983.             v[0] = 0;
  984.         else if (index == 3 || index == 5)
  985.             v[1] = 0;
  986.         else
  987.             sameScale = True;
  988.     }
  989.  
  990.     width   = pw->paint.region.startScaleX * pw->paint.region.orig.width;
  991.     height  = pw->paint.region.startScaleY * pw->paint.region.orig.height;
  992.  
  993.     createVtxPts(pw, ovtx, True, False);
  994.  
  995.     pw->paint.region.scaleX = (float)((int)( width + v[0]))/pw->paint.region.orig.width;
  996.     pw->paint.region.scaleY = (float)((int)(height + v[1]))/pw->paint.region.orig.height;
  997.  
  998.     if (sameScale) {
  999.         float    sx = ABS(pw->paint.region.scaleX);
  1000.         float    sy = ABS(pw->paint.region.scaleY);
  1001.  
  1002.         sx = MIN(sx, sy);
  1003.  
  1004.         pw->paint.region.scaleX = SIGN(pw->paint.region.scaleX) * sx;
  1005.         pw->paint.region.scaleY = SIGN(pw->paint.region.scaleY) * sx;
  1006.     }
  1007.  
  1008.     MKMAT(pw);
  1009.     createVtxPts(pw, nvtx, True, False);
  1010.  
  1011.     fp = pw->paint.region.fixedPoint;
  1012.     dx = ovtx[fp][0] - nvtx[fp][0];
  1013.     dy = ovtx[fp][1] - nvtx[fp][1];
  1014.  
  1015.     pw->paint.region.centerX += dx;
  1016.     pw->paint.region.centerY += dy;
  1017.  
  1018.     pw->paint.region.needResize = True;
  1019.     regionRedraw(pw);
  1020. }
  1021.  
  1022. /*
  1023. **
  1024. **
  1025. */
  1026. static void writeRegion(PaintWidget pw)
  1027. {
  1028.     GC        gc;
  1029.     Pixmap        pix, src;
  1030.     XRectangle    nr;
  1031.     int        zoom = GET_ZOOM(pw);
  1032.  
  1033.     if (!pw->paint.region.isVisible)
  1034.         return;
  1035.     /*
  1036.     **  No need to write it, it's still on the drawable.
  1037.     */
  1038.     if (pw->paint.region.isAttached)
  1039.         return;
  1040.  
  1041.     nr.x      = pw->paint.region.rect.x;
  1042.     nr.y      = pw->paint.region.rect.y;
  1043.     nr.width  = pw->paint.region.rect.width / zoom;
  1044.     nr.height = pw->paint.region.rect.height / zoom;
  1045.  
  1046.     /*
  1047.     **  If we've already modified the background (aka ripped the
  1048.     **    image up.  Then don't get a new undo buffer.
  1049.     **    The second case of the if is if you've done an undo inbetween.
  1050.     */
  1051.     if (pw->paint.region.undoPixmap == None ||
  1052.         pw->paint.region.undoPixmap != GET_PIXMAP(pw))
  1053.         pix = PwUndoStart((Widget)pw, &nr);
  1054.     else
  1055.         pix = pw->paint.region.undoPixmap;
  1056.  
  1057.     PwUndoAddRectangle((Widget)pw, &nr);
  1058.  
  1059.     if (pw->paint.region.fg_gc != None) {
  1060.         gc = pw->paint.region.fg_gc;
  1061.         XSetClipOrigin(XtDisplay(pw), gc, nr.x, nr.y);
  1062.     } else {
  1063.         gc = pw->paint.tgc;
  1064.     }
  1065.  
  1066.     if (pw->paint.region.proc != NULL) {
  1067.         XImage    *sim = pw->paint.region.sourceImg;
  1068.         Boolean    made = False;
  1069.  
  1070.         if (sim == NULL) {
  1071.             sim = XGetImage(XtDisplay(pw), 
  1072.                     pw->paint.region.source, 0, 0, 
  1073.                     pw->paint.region.orig.width, 
  1074.                     pw->paint.region.orig.height, AllPlanes, ZPixmap);
  1075.             made = True;
  1076.         }
  1077.  
  1078.         src = (*pw->paint.region.proc)((Widget)pw, sim, pw->paint.region.mat);
  1079.         if (made)
  1080.             XDestroyImage(sim);
  1081.     } else {
  1082.         src = pw->paint.region.source;
  1083.     }
  1084.  
  1085.     XCopyArea(XtDisplay(pw), src, pix, gc,
  1086.             0, 0,
  1087.             nr.width, nr.height,
  1088.             nr.x, nr.y);
  1089.  
  1090.     if (pw->paint.region.fg_gc != None)
  1091.         XSetClipOrigin(XtDisplay(pw), gc, 0, 0);
  1092.  
  1093.     PwUpdate((Widget)pw, &nr, False);
  1094. }
  1095.  
  1096. /*
  1097. **  Called when the parent widgets zoom factor changes
  1098. */
  1099. static void zoomValueChanged(Widget w, XtPointer junk1, XtPointer junk2)
  1100. {
  1101.     PaintWidget    pw = (PaintWidget)w;
  1102.     int        zoom = GET_ZOOM(pw);
  1103.     int        nx, ny;
  1104.     int        nw, nh;
  1105.  
  1106.     if (zoom == pw->paint.region.curZoom)
  1107.         return;
  1108.  
  1109.     if (!pw->paint.region.isVisible)
  1110.         return;
  1111.  
  1112.     nx = (pw->paint.region.rect.x - pw->paint.zoomX) * zoom;
  1113.     ny = (pw->paint.region.rect.y - pw->paint.zoomY) * zoom;
  1114.     nw = (pw->paint.region.rect.width / pw->paint.region.curZoom) * zoom;
  1115.     pw->paint.region.rect.width = nw;
  1116.     nh = (pw->paint.region.rect.height / pw->paint.region.curZoom) * zoom;
  1117.     pw->paint.region.rect.height = nh;
  1118.     pw->paint.region.curZoom = zoom;
  1119.  
  1120.     XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
  1121.     XtResizeWidget(pw->paint.region.child, nw, nh, BW);
  1122.  
  1123.     pw->paint.region.needResize = True;
  1124.     moveGrips(pw);
  1125.     regionRedraw(pw);
  1126. }
  1127.  
  1128. void    pwRegionZoomPosChanged(PaintWidget pw)
  1129. {
  1130.     int    nx, ny;
  1131.     int    zoom;
  1132.  
  1133.     if (!pw->paint.region.isVisible)
  1134.         return;
  1135.  
  1136.     zoom = GET_ZOOM(pw);
  1137.  
  1138.     nx = (pw->paint.region.rect.x - pw->paint.zoomX) * zoom;
  1139.     ny = (pw->paint.region.rect.y - pw->paint.zoomY) * zoom;
  1140.  
  1141.     XtMoveWidget(pw->paint.region.child, nx - BW, ny - BW);
  1142. }
  1143.  
  1144.  
  1145. static void writeCleanRegion(PaintWidget pw, Boolean flag)
  1146. {
  1147.     if (!pw->paint.region.isVisible)
  1148.         return;
  1149.  
  1150.     writeRegion(pw);
  1151.     
  1152.     /*
  1153.     **  Free up temporary images
  1154.     */
  1155.     if (pw->paint.region.sourceImg != NULL)
  1156.         XDestroyImage(pw->paint.region.sourceImg);
  1157.     if (pw->paint.region.maskImg != NULL)
  1158.         XDestroyImage(pw->paint.region.maskImg);
  1159.  
  1160.     pw->paint.region.sourceImg     = NULL;
  1161.     pw->paint.region.maskImg       = NULL;
  1162.     if (pw->paint.region.source != None) {
  1163.         XFreePixmap(XtDisplay(pw), pw->paint.region.source);
  1164.         pw->paint.region.source = None;
  1165.     }
  1166.     if (pw->paint.region.mask != None) {
  1167.         XFreePixmap(XtDisplay(pw), pw->paint.region.mask);
  1168.         pw->paint.region.mask = None;
  1169.     }
  1170.     if (pw->paint.region.notMask != None) {
  1171.         XFreePixmap(XtDisplay(pw), pw->paint.region.notMask);
  1172.         pw->paint.region.notMask = None;
  1173.     }
  1174.  
  1175.     pw->paint.region.undoPixmap = None;
  1176.  
  1177.     if (flag) {
  1178.         if (pw->paint.region.child != None) 
  1179.             XtUnmapWidget(pw->paint.region.child);
  1180.         pw->paint.region.isVisible = False;
  1181.     }
  1182. }
  1183.  
  1184. void PwRegionFinish(Widget w, Boolean flag)
  1185. {
  1186.     PaintWidget    pw = (PaintWidget)w;
  1187.     PaintWidget    pp = (PaintWidget)pw->paint.paint;
  1188.     PaintWidget    tpw;
  1189.     int        i;
  1190.  
  1191.     if (flag) {
  1192.         tpw = (pp == None) ? pw : pp;
  1193.         writeCleanRegion(tpw, True);
  1194.         for (i = 0; i < tpw->paint.paintChildrenSize; i++)
  1195.             writeCleanRegion((PaintWidget)tpw->paint.paintChildren[i], True);
  1196.         doCallbacks(pw, False);
  1197.     } else {
  1198.         writeCleanRegion(pw, True);
  1199.     }
  1200. }
  1201.  
  1202. void PwRegionSet(Widget w, XRectangle *rect, Pixmap pix, Pixmap mask)
  1203. {
  1204.     PaintWidget    pw = (PaintWidget)w;
  1205.     int        i;
  1206.     int        nx, ny, x, y, width, height;
  1207.     int        zoom = GET_ZOOM(pw);
  1208.     Boolean        setIsAttached = False;
  1209.  
  1210.     /*
  1211.     **  If there is an image, write it
  1212.     **     rect == NULL, then this is just a "write" & "unmap" request
  1213.     */
  1214.     PwRegionFinish(w, True);
  1215.     if (rect == NULL) 
  1216.         return;
  1217.  
  1218.     pw->paint.region.curZoom = zoom;
  1219.  
  1220.     x = (rect->x + pw->paint.zoomX) * zoom;
  1221.     y = (rect->y + pw->paint.zoomY) * zoom;
  1222.     width  = rect->width * zoom;
  1223.     height = rect->height * zoom;
  1224.  
  1225.     /*
  1226.     **  A little "initializing"
  1227.     */
  1228.     pw->paint.region.isDrawn    = False;
  1229.     pw->paint.region.isTracking = False;
  1230.     pw->paint.region.needResize = False;
  1231.  
  1232.     pw->paint.region.rect = *rect;
  1233.  
  1234.     if (pix == None) {
  1235.         setIsAttached = True;
  1236.  
  1237.         if (rect->x < 0) {
  1238.             rect->width += rect->x;
  1239.             rect->x = 0;
  1240.         }
  1241.         if (rect->y < 0) {
  1242.             rect->height += rect->y;
  1243.             rect->y = 0;
  1244.         }
  1245.         if (rect->width > pw->paint.drawWidth)
  1246.             rect->width = pw->paint.drawWidth;
  1247.         if (rect->height > pw->paint.drawHeight)
  1248.             rect->height = pw->paint.drawHeight;
  1249.  
  1250.         pw->paint.region.source = XCreatePixmap(XtDisplay(pw), XtWindow(pw), 
  1251.                             rect->width, rect->height,
  1252.                             pw->core.depth);
  1253.         XCopyArea(XtDisplay(pw), GET_PIXMAP(pw), pw->paint.region.source,
  1254.                     pw->paint.gc,
  1255.                     rect->x, rect->y, 
  1256.                     rect->width, rect->height, 
  1257.                     0, 0);
  1258.     } else {
  1259.         pw->paint.region.source = pix;
  1260.     }
  1261.  
  1262.     pw->paint.region.mask = mask;
  1263.     pw->paint.region.orig = *rect;
  1264.  
  1265.     /*
  1266.     **  If there is a clipping mask, create a fg and bg GC with clip-masks
  1267.     **    to draw through.
  1268.     */
  1269.     if (mask != None) {
  1270.  
  1271.         if (pw->paint.region.fg_gc == None) {
  1272.             pw->paint.region.fg_gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, 0);
  1273.             pw->paint.region.bg_gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, 0);
  1274.         }
  1275.  
  1276.         /*
  1277.         **  Make sure the Mask GC is built.
  1278.         */
  1279.         GET_MGC(pw, mask);
  1280.         regionCreateNotMask(pw);
  1281.     } else {
  1282.         /*
  1283.         **  No clip mask, make sure we aren't using one.
  1284.         */
  1285.         if (pw->paint.region.fg_gc != None) {
  1286.             XSetClipMask(XtDisplay(w), pw->paint.region.fg_gc, None);
  1287.             XSetClipMask(XtDisplay(w), pw->paint.region.bg_gc, None);
  1288.         }
  1289.     }
  1290.  
  1291.     if (pw->paint.region.child == None) {
  1292.         pw->paint.region.child = XtVaCreateWidget("region",
  1293.                     compositeWidgetClass, w,
  1294.                     XtNborderWidth, BW,
  1295.                     NULL);
  1296.         XtAddEventHandler(pw->paint.region.child, ButtonPressMask, 
  1297.                 False, 
  1298.                 (XtEventHandler)regionButtonPress, 
  1299.                 (XtPointer)pw);
  1300.         XtAddEventHandler(pw->paint.region.child, ButtonReleaseMask, 
  1301.                 False, 
  1302.                 (XtEventHandler)regionButtonRelease, 
  1303.                 (XtPointer)pw);
  1304.         XtAddEventHandler(pw->paint.region.child, ButtonMotionMask, 
  1305.                 False, 
  1306.                 (XtEventHandler)regionGrab, 
  1307.                 (XtPointer)pw);
  1308.         XtAddEventHandler(pw->paint.region.child, ExposureMask, 
  1309.                 False, 
  1310.                 (XtEventHandler)regionExpose, 
  1311.                 (XtPointer)pw);
  1312.         XtAddEventHandler(pw->paint.region.child, StructureNotifyMask, 
  1313.                 False, 
  1314.                 (XtEventHandler)regionMove, 
  1315.                 (XtPointer)pw);
  1316.         XtAddCallback((Widget)pw, XtNsizeChanged, (XtCallbackProc)zoomValueChanged, (XtPointer)NULL);
  1317.         XtVaSetValues(pw->paint.region.child, XtNx, x, XtNy, y, XtNwidth, width, XtNheight, height, NULL);
  1318.         XtManageChild(pw->paint.region.child);
  1319.         XDefineCursor(XtDisplay(w), XtWindow(pw->paint.region.child), XCreateFontCursor(XtDisplay(w), XC_fleur));
  1320.  
  1321.         for (i = 0; i < 9; i++) {
  1322.             if (i == 4)
  1323.                 continue;
  1324.  
  1325.             pw->paint.region.grip[i] = XtVaCreateManagedWidget("grip",
  1326.                         gripWidgetClass, pw->paint.region.child,
  1327.                         XtNwidth, 6,
  1328.                         XtNheight, 6,
  1329.                         NULL);
  1330.  
  1331.             XtAddEventHandler(pw->paint.region.grip[i], ButtonPressMask, 
  1332.                     False, 
  1333.                     (XtEventHandler)gripPress, 
  1334.                     (XtPointer)pw);
  1335.             XtAddEventHandler(pw->paint.region.grip[i], ButtonMotionMask, 
  1336.                     False, 
  1337.                     (XtEventHandler)gripGrab, 
  1338.                     (XtPointer)pw);
  1339.             XtAddEventHandler(pw->paint.region.grip[i], ButtonReleaseMask, 
  1340.                     False, 
  1341.                     (XtEventHandler)gripRelease, 
  1342.                     (XtPointer)pw);
  1343.         }
  1344.         regionSetGripCursors(pw);
  1345.         pw->paint.region.isVisible = True;
  1346.     } else {
  1347.         XClearArea(XtDisplay(pw), XtWindow(pw->paint.region.child),
  1348.                 0, 0, 0, 0, True);
  1349.     }
  1350.  
  1351.     if (setIsAttached) {
  1352.         nx = rect->x;
  1353.         ny = rect->y;
  1354.     } else {
  1355.         nx = pw->paint.downX;
  1356.         ny = pw->paint.downY;
  1357.     }
  1358.     pw->paint.region.rect.x = nx;
  1359.     pw->paint.region.rect.y = ny;
  1360.     nx -= pw->paint.zoomX;
  1361.     ny -= pw->paint.zoomY;
  1362.     XtVaSetValues(pw->paint.region.child, XtNx, nx * zoom - BW, 
  1363.                           XtNy, ny * zoom - BW, 
  1364.                           XtNwidth, width, 
  1365.                           XtNheight, height, 
  1366.                           NULL);
  1367.  
  1368.     pw->paint.region.scaleX  = 1.0;
  1369.     pw->paint.region.scaleY  = 1.0;
  1370.     pw->paint.region.centerX = pw->paint.region.orig.width / 2;
  1371.     pw->paint.region.centerY = pw->paint.region.orig.height / 2;
  1372.     COPY_MAT(matIdentity, pw->paint.region.rotMat);
  1373.     MKMAT(pw);
  1374.  
  1375.     if (zoom != 1) {
  1376.         pw->paint.region.rect.width  *= zoom;
  1377.         pw->paint.region.rect.height *= zoom;
  1378.     }
  1379.  
  1380.     moveGrips(pw);
  1381.  
  1382.     if (!pw->paint.region.isVisible) {
  1383.         XtMapWidget(pw->paint.region.child);
  1384.         pw->paint.region.isVisible = True;
  1385.     }
  1386.  
  1387. #ifdef SHAPE
  1388.     XShapeCombineMask(XtDisplay(pw), XtWindow(pw->paint.region.child), 
  1389.                     ShapeBounding, 0, 0, None, ShapeSet);
  1390. #endif
  1391.  
  1392.     pw->paint.region.isAttached = setIsAttached;
  1393.     doCallbacks(pw, True);
  1394. }
  1395.  
  1396. static PaintWidget getActiveRegion(PaintWidget pw)
  1397. {
  1398.     PaintWidget    pp = (PaintWidget)pw->paint.paint;
  1399.     PaintWidget    tpw = (pp == None) ? pw : pp;
  1400.     int        i;
  1401.  
  1402.     if (pw->paint.region.isVisible && pw->paint.region.source != None)
  1403.         return pw;
  1404.  
  1405.     if (tpw->paint.region.isVisible && tpw->paint.region.source != None)
  1406.         return tpw;
  1407.  
  1408.     for (i = 0; i < tpw->paint.paintChildrenSize; i++) {
  1409.         PaintWidget    p = (PaintWidget)tpw->paint.paintChildren[i];
  1410.         if (p->paint.region.source != None) 
  1411.             return p;
  1412.     }
  1413.  
  1414.     return None;
  1415. }
  1416.  
  1417. void PwRegionSetRawPixmap(Widget w, Pixmap pix)
  1418. {
  1419.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1420.  
  1421.     if (pw == None)
  1422.         return;
  1423.  
  1424.     XFreePixmap(XtDisplay(pw), pw->paint.region.source);
  1425.  
  1426.     if (pw->paint.region.sourceImg != NULL) {
  1427.         XDestroyImage(pw->paint.region.sourceImg);
  1428.         pw->paint.region.sourceImg = NULL;
  1429.     }
  1430.  
  1431.     pw->paint.region.source    = pix;
  1432.  
  1433.     doResize(pw);
  1434.     regionRedraw(pw);
  1435. }
  1436.  
  1437. Boolean PwRegionGet(Widget w, Pixmap *pix, Pixmap *mask)
  1438. {
  1439.     Display        *dpy = XtDisplay(w);
  1440.     Window        win  = XtWindow(w);
  1441.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1442.     Pixmap        myMask = None, notMask = None;
  1443.     int        zoom;
  1444.     int        width, height;
  1445.  
  1446.     if (pw == None)
  1447.         return False;
  1448.     zoom = GET_ZOOM(pw);
  1449.     width  = pw->paint.region.orig.width;
  1450.     height = pw->paint.region.orig.height;
  1451.  
  1452.     if (pix)  *pix  = None;
  1453.     if (mask) *mask = None;
  1454.  
  1455.     if (pw->paint.region.source != None && pix != NULL) {
  1456.         *pix = XCreatePixmap(dpy, win, width, height, pw->core.depth);
  1457.         if (pw->paint.region.sourceImg != NULL) {
  1458.             XPutImage(dpy, *pix, pw->paint.tgc,
  1459.                 pw->paint.region.sourceImg, 
  1460.                 0, 0, 0, 0, width, height);
  1461.         } else {
  1462.             XCopyArea(dpy, pw->paint.region.source, 
  1463.                 *pix, pw->paint.tgc,
  1464.                 0, 0, width, height, 0, 0);
  1465.         }
  1466.     }
  1467.     if (pw->paint.region.mask != None) {
  1468.         myMask  = XCreatePixmap(dpy, win, width, height, 1);
  1469.         notMask = XCreatePixmap(dpy, win, width, height, 1);
  1470.  
  1471.         if (pw->paint.region.maskImg != NULL) {
  1472.             XPutImage(dpy, myMask, pw->paint.mgc,
  1473.                 pw->paint.region.maskImg, 
  1474.                 0, 0, 0, 0, width, height);
  1475.         } else {
  1476.             XCopyArea(dpy, pw->paint.region.mask, 
  1477.                 myMask, pw->paint.mgc,
  1478.                 0, 0, width, height, 0, 0);
  1479.         }
  1480.  
  1481.         XSetFunction(dpy, pw->paint.mgc, GXcopyInverted);
  1482.         XCopyArea(dpy, myMask, notMask, pw->paint.mgc, 0, 0, 
  1483.                 width, height, 0, 0);
  1484.         XSetFunction(dpy, pw->paint.mgc, GXcopy);
  1485.  
  1486.         if (mask == NULL)
  1487.             XFreePixmap(dpy, myMask);
  1488.         else
  1489.             *mask = myMask;
  1490.     }
  1491.     if (notMask != None && pix != NULL) {
  1492.         XSetClipOrigin(dpy, pw->paint.igc, 0, 0);
  1493.         XSetClipMask(XtDisplay(pw), pw->paint.igc, notMask);
  1494.         XFillRectangle(XtDisplay(pw), *pix, pw->paint.igc, 0, 0,
  1495.                 width, height);
  1496.         XSetClipMask(XtDisplay(pw), pw->paint.igc, None);
  1497.  
  1498.     }
  1499.  
  1500.     if (notMask != None)
  1501.         XFreePixmap(dpy, notMask);
  1502.  
  1503.     return True;
  1504. }
  1505.  
  1506. void PwRegionClear(Widget w)
  1507. {
  1508.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1509.  
  1510.     if (pw == None)
  1511.         return;
  1512.  
  1513.     PwRegionTear(w);
  1514.  
  1515.     pw->paint.region.isVisible = False;
  1516.     if (pw->paint.region.child != None)
  1517.         XtUnmapWidget(pw->paint.region.child);
  1518.     doCallbacks(pw, False);
  1519. }
  1520.  
  1521. /*
  1522. **  Insure that the current region is not connected to the
  1523. **    source pixmap.
  1524. */
  1525. void PwRegionTear(Widget w)
  1526. {
  1527.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1528.     XRectangle    nr;
  1529.  
  1530.     if (pw == None)
  1531.         return;
  1532.     if (!pw->paint.region.isAttached || !pw->paint.region.isVisible)
  1533.         return;
  1534.  
  1535.     nr = pw->paint.region.rect;
  1536.     nr.width  /= GET_ZOOM(pw);
  1537.     nr.height /= GET_ZOOM(pw);
  1538.  
  1539.     pw->paint.region.undoPixmap = PwUndoStart((Widget)pw, &nr);
  1540.  
  1541.     if (pw->paint.region.mask != None) {
  1542.         XSetClipOrigin(XtDisplay(pw), pw->paint.igc, nr.x, nr.y);
  1543.         XSetClipMask(XtDisplay(pw), pw->paint.igc, pw->paint.region.mask);
  1544.     }
  1545. #if 0
  1546.     if (pw->paint.sourcePixmap == None) {
  1547. #endif
  1548.         XFillRectangles(XtDisplay(pw), pw->paint.region.undoPixmap,
  1549.             pw->paint.igc, &nr, 1);
  1550. #if 0
  1551.     } else {
  1552.         XCopyArea(XtDisplay(pw), pw->paint.sourcePixmap, pw->paint.region.undoPixmap, 
  1553.                 pw->paint.igc, 
  1554.                 nr.x, nr.y,
  1555.                 nr.width, nr.height,
  1556.                 nr.x, nr.y);
  1557.     }
  1558. #endif
  1559.     PwUpdate((Widget)pw, &nr, False);
  1560.     if (pw->paint.region.mask != None) {
  1561.         XSetClipOrigin(XtDisplay(pw), pw->paint.igc, 0, 0);
  1562.         XSetClipMask(XtDisplay(pw), pw->paint.igc, None);
  1563.     }
  1564.     pw->paint.region.isAttached = False;
  1565. }
  1566.  
  1567. void PwRegionAppendMatrix(Widget w, pwMatrix mat)
  1568. {
  1569.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1570.  
  1571.     if (pw == None)
  1572.         return;
  1573.     
  1574.     PwRegionTear((Widget)pw);
  1575.  
  1576.     mm(pw->paint.region.rotMat, mat, pw->paint.region.rotMat);
  1577.     MKMAT(pw);
  1578.  
  1579.     pw->paint.region.needResize = True;
  1580.     regionResizeWindow(pw, False);
  1581.     regionRedraw(pw);
  1582. }
  1583.  
  1584. void PwRegionSetMatrix(Widget w, pwMatrix mat)
  1585. {
  1586.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1587.  
  1588.     if (pw == None)
  1589.         return;
  1590.  
  1591.     PwRegionTear((Widget)pw);
  1592.  
  1593.     COPY_MAT(mat, pw->paint.region.rotMat);
  1594.     MKMAT(pw);
  1595.  
  1596.     pw->paint.region.needResize = True;
  1597.     regionResizeWindow(pw, False);
  1598.     regionRedraw(pw);
  1599. }
  1600.  
  1601. void PwRegionAddScale(Widget w, float *xs, float *ys)
  1602. {
  1603.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1604.  
  1605.     if (pw == None)
  1606.         return;
  1607.  
  1608.     if (xs != NULL)
  1609.         pw->paint.region.scaleX *= *xs;
  1610.     if (ys != NULL)
  1611.         pw->paint.region.scaleY *= *ys;
  1612.  
  1613.     MKMAT(pw);
  1614.  
  1615.     pw->paint.region.needResize = True;
  1616.     regionResizeWindow(pw, False);
  1617.     regionRedraw(pw);
  1618. }
  1619.  
  1620. void PwRegionSetScale(Widget w, float *xs, float *ys)
  1621. {
  1622.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1623.  
  1624.     if (pw == None)
  1625.         return;
  1626.  
  1627.     if (xs != NULL)
  1628.         pw->paint.region.scaleX = *xs;
  1629.     if (ys != NULL)
  1630.         pw->paint.region.scaleY = *ys;
  1631.  
  1632.     MKMAT(pw);
  1633.  
  1634.     pw->paint.region.needResize = True;
  1635.     regionResizeWindow(pw, False);
  1636.     regionRedraw(pw);
  1637. }
  1638.  
  1639. void PwRegionReset(Widget w, Boolean flag)
  1640. {
  1641.     PaintWidget    pw = getActiveRegion((PaintWidget)w);
  1642.     pwMatrix    mat;
  1643.  
  1644.     if (pw == None)
  1645.         return;
  1646.  
  1647.     PwRegionTear((Widget)pw);
  1648.  
  1649.     mat[0][0] = mat[1][1] = 1;
  1650.     mat[1][0] = mat[0][1] = 0;
  1651.  
  1652.     COPY_MAT(mat, pw->paint.region.rotMat);
  1653.     pw->paint.region.scaleY = 1.0;
  1654.     pw->paint.region.scaleX = 1.0;
  1655.     MKMAT(pw);
  1656.  
  1657.     pw->paint.region.needResize = True;
  1658.     regionResizeWindow(pw, False);
  1659.     regionRedraw(pw);
  1660.  
  1661.     /* XXX flag should reset X & Y position as well */
  1662. }
  1663.